package edu.lingnan.blog.auth.config;

import edu.lingnan.blog.common.core.constant.RabbitMQConstants;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.annotation.EnableRabbit;
import org.springframework.amqp.rabbit.config.RetryInterceptorBuilder;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.retry.RejectAndDontRequeueRecoverer;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.retry.RetryCallback;
import org.springframework.retry.RetryContext;
import org.springframework.retry.RetryListener;
import org.springframework.retry.backoff.ExponentialBackOffPolicy;
import org.springframework.retry.policy.SimpleRetryPolicy;
import org.springframework.retry.support.RetryTemplate;

/**
 * @author cql
 */
@Configuration
@EnableRabbit
public class RabbitMQConfig {


    /**
     * TODO 引入MQ需要解决的问题
     * (1)解决消息丢失问题
     * (2)消息重复带来的幂等性为你
     * (3)消息挤压问题
     * (4)消息有序性问题
     * (5)交换机，队列持久化?
     */


    @Autowired
    private ConnectionFactory rabbitConnectionFactory;

    /**
     * https://blog.csdn.net/weixin_43606226/article/details/114372349
     * 存在此名字的bean 自带的容器工厂会不加载（yml下rabbitmq下的template的配置），如果想自定义来区分开 需要改变bean 的名称
     *
     * @return
     */
    @Bean
    public RabbitTemplate rabbitTemplate() {
        RabbitTemplate rabbitTemplate = new RabbitTemplate(rabbitConnectionFactory);
        //默认是用jdk序列化
        //数据转换为json存入消息队列，方便可视化界面查看消息数据
        rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
        //设置开启Mandatory,才能触发回调函数,无论消息推送结果怎么样都强制调用回调函数
        rabbitTemplate.setMandatory(true);

        /**
         * publisher-confirms: true
         * 通过实现 ConfirmCallback 接口，消息发送到 Broker 后触发回调，确认消息是否到达 Broker 服务器，也就是只确认是否正确到达 Exchange 中
         * 如果 b 为false，说明消息没有到达 Exchange
         */
        rabbitTemplate.setConfirmCallback(
                (correlationData, b, s) -> {
                    System.out.println("ConfirmCallback     " + "相关数据：" + correlationData);
                    System.out.println("ConfirmCallback     " + "确认情况：" + b);
                    System.out.println("ConfirmCallback     " + "原因：" + s);
                });

        /**
         * publisher-returns: true
         * 通过实现 ReturnCallback 接口，启动消息失败返回，比如路由不到队列时触发回调
         */
        rabbitTemplate.setReturnCallback((message, i, s, s1, s2) -> {
            System.out.println("ReturnCallback：     " + "消息：" + message);
            System.out.println("ReturnCallback：     " + "回应码：" + i);
            System.out.println("ReturnCallback：     " + "回应消息：" + s);
            System.out.println("ReturnCallback：     " + "交换机：" + s1);
            System.out.println("ReturnCallback：     " + "路由键：" + s2);
        });

        // 此处设置重试template后，会再生产者发送消息的时候，调用该template中的调用链
        // rabbitTemplate.setRetryTemplate(rabbitRetryTemplate());


        return rabbitTemplate;
    }


    /**
     * 存在此名字的bean 自带的容器工厂会不加载（yml下rabbitmq下的listener下的simple配置），如果想自定义来区分开 需要改变bean 的名称
     *
     * @return
     */
    @Bean
    public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory() {
        SimpleRabbitListenerContainerFactory containerFactory = new SimpleRabbitListenerContainerFactory();
        containerFactory.setConnectionFactory(rabbitConnectionFactory);

        // 并发消费者数量
        containerFactory.setConcurrentConsumers(1);
        containerFactory.setMaxConcurrentConsumers(20);
        // 预加载消息数量 -- QOS
        containerFactory.setPrefetchCount(1);
        /**
         * 应答模式（此处设置为手动）
         * 手动ACK，必须要 channel.basicAck() 才能将消息从队列中删除
         */
        containerFactory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
        //消息序列化方式
        containerFactory.setMessageConverter(new Jackson2JsonMessageConverter());

        // containerFactory.setAdviceChain(
        //         RetryInterceptorBuilder
        //                 .stateless()
        //                 .recoverer(new RejectAndDontRequeueRecoverer())
        //                 .retryOperations(rabbitRetryTemplate())
        //                 .build()
        // );


        return containerFactory;
    }


    /**
     * 声明直连交换机 支持持久化.
     *
     * @return the exchange
     */
    @Bean(RabbitMQConstants.LOGIN_EXCHANGE)
    public Exchange directExchange() {
        return ExchangeBuilder.directExchange(RabbitMQConstants.LOGIN_EXCHANGE).durable(true).build();
    }

    @Bean(RabbitMQConstants.LOGIN_QUEUE)
    public Queue directQueue() {
        return new Queue(RabbitMQConstants.LOGIN_QUEUE, true, true, true);
    }

    @Bean
    public Binding directBinding(@Qualifier(RabbitMQConstants.LOGIN_QUEUE) Queue queue, @Qualifier(RabbitMQConstants.LOGIN_EXCHANGE) Exchange directExchange) {
        return BindingBuilder.bind(queue).to(directExchange).with(RabbitMQConstants.LOGIN_ROUTING_KEY).noargs();
    }


}
